﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Qurihara;
using System.Drawing;
using Qurihara.OSCCom;
using Qurihara.QBasic;
using System.Windows.Forms;

namespace Qurihara.OSCCom
{
    public class Server
    {
        protected IKotodamaBase kotodama;
        public static string QRPrefix = "unryu.org:";

        private ClientList clientList;
        public ClientList ClientList
        {
            get { return clientList; }
        }
        private Hashtable displayTable; // BitmapElementからclientを引ける。
        //public Hashtable DisplayTable
        //{
        //    get { return displayTable; }
        //}
        private ArrayList displayGroupList;  // GraphListが入っている。
        public ArrayList DisplayGroupList
        {
            get { return displayGroupList; }
        }

        public Server()
        {
            clientList = new ClientList();
            displayTable = new Hashtable();
            displayGroupList = new ArrayList();
        }

        private void InitClient()
        {
            clientList.Dispose();
            clientList.Clear();
            displayTable.Clear();
            foreach (ArrayList al in displayGroupList) al.Clear();
            displayGroupList.Clear();
        }

        public void Configure(IKotodamaBase _kotodama)
        {
            kotodama = _kotodama;

            InitClient();
            
            Qurihara.QBasic.ProgressBarForm pbf = new ProgressBarForm("参加者の接続をチェックしています。");
            pbf.Show();
            foreach (FigureElement fe in kotodama.Presentation.CurrentSlide.ElementList)
            {
                pbf.ProgressBar.Value = 100 * kotodama.Presentation.CurrentSlide.ElementList.IndexOf(fe)
                    / (kotodama.Presentation.CurrentSlide.ElementList.Count + 1);
                pbf.Refresh();
                //QRCodeDecoder decoder = new QRCodeDecoder();
                if (fe is BitmapElement)
                {
                    BitmapElement be = fe as BitmapElement;
                    //String decodedString = "";
                    //object o = bitmapQRTable[be];
                    //if (o == null)
                    //{
                        if (be.Tag != "")
                        {
                            Decode(be.Tag, be);
                            //bitmapQRTable.Add(be, be.Tag);
                        }
                        //else
                        //{
                        //    bitmapQRTable.Add(be, "");

                        //}
                        //int[][] image = null;
                        //image = QRCodeUtility.parseImage(be.BitMap);
                        //try
                        //{
                        //    decodedString = decoder.decode(image);
                        //    Decode(decodedString, be);
                        //    bitmapQRTable.Add(be, decodedString);
                        //}
                        //catch (DecodingFailedException ex)
                        //{
                        //    bitmapQRTable.Add(be, "");
                        //    //decodedString = ex.Message;
                        //}
                        ////MessageBox.Show(decodedString);
                    //}
                    //else
                    //{
                    //    try
                    //    {
                    //        decodedString = (String)o;
                    //        if (decodedString != "")
                    //            Decode(decodedString, be);
                    //    }
                    //    catch { }
                    //}
                }
            }
            pbf.Close();
            pbf.Dispose();
            MakeGroup();
            MakeGraph();
            ReplaceBm2Name();
        }

        private bool Decode(string qrstring, BitmapElement be)
        {
            if (qrstring.StartsWith(QRPrefix))
            {
                string qrs = qrstring.Substring(QRPrefix.Length);
                //if (qrs == "provider")
                //{
                //    displayTable.Add(be, "provider");
                //}
                //else
                //{
                    string[] data = qrs.Split(',');
                    string ip = (data[0].Split('='))[1];
                    int port = int.Parse((data[1].Split('='))[1]);
                    string name = (data[2].Split('='))[1];
                    string role = (data[3].Split('='))[1];
                    //ClientWithBitmapElement cl = RegisterClient(be.BoundingBox, ip, port, name,role);
                    ClientWithBitmapElement cl = RegisterClient(be, ip, port, name, role);
                    if (cl == null) return false;
                    displayTable.Add(be, cl);
                //}
                return true;
            }
            return false;
        }

        private ClientWithBitmapElement RegisterClient(BitmapElement be, string ip, int port, string name,string role)
        {
            ClientWithBitmapElement.RoleType rt = ClientWithBitmapElement.GetRoleTypeFromString(role);

            IComCore core;
            switch (rt)
            {
                case ClientWithBitmapElement.RoleType.Presenter:
                case ClientWithBitmapElement.RoleType.Screen:
                    core = new TCPComCore();
                    break;
                case ClientWithBitmapElement.RoleType.Student:
                default:
                    core = new WebComCore();
                    break;
            }
            //TCPComCore core = new TCPComCore();
            //OSCComCore core = new OSCComCore();
            //WebComCore core = new WebComCore();
            ClientWithBitmapElement cl = new ClientWithBitmapElement(be,core, ip, port, name, rt, this);//clientList);

            if (cl.InitRole())
            {
                clientList.Add(cl);
            }
            else
            {
                MessageBox.Show(cl.Name + " に接続できませんでした。");
                cl.Dispose();
                cl = null;
            }
            return cl;
        }

        #region group
        private void MakeGroup()
        {
            if (displayTable.Count < 2) return;
            ArrayList bmList = new ArrayList();
            IEnumerator enume = displayTable.Keys.GetEnumerator();
            while (enume.MoveNext())
            {
                bmList.Add(enume.Current);
            }
            for (int i = 0; i < bmList.Count; i++)
            {
                bool grouped = false;
                BitmapElement bm1 = (BitmapElement)bmList[i];
                Rectangle r1 = bm1.BoundingBox;
                for (int j = i + 1; j < bmList.Count; j++)
                {
                    BitmapElement bm2 = (BitmapElement)bmList[j];
                    Rectangle r2 = bm2.BoundingBox;
                    if (r1.IntersectsWith(r2))
                    {
                        TheyAreGroup(bm1, bm2);
                        grouped = true;
                    }
                }
                if (!grouped)
                {
                    GraphList list = new GraphList();
                    //ArrayList list = new ArrayList();
                    list.Add(bmList[i]);
                    displayGroupList.Add(list);
                }
            }
        }
        private void TheyAreGroup(object bm1, object bm2)
        {
            GraphList containedGroup1 = IsContained(bm1);
            GraphList containedGroup2 = IsContained(bm2);

            if (containedGroup1 == null)
            {
                if (containedGroup2 == null)
                {//二つとも新顔-->新たなグループ
                    //ArrayList al = new ArrayList();
                    GraphList al = new GraphList();
                    al.Add(bm1);
                    al.Add(bm2);
                    displayGroupList.Add(al);
                }
                else
                {//1だけ新顔-->2のグループに吸収
                    containedGroup2.Add(bm1);
                }
            }
            else
            {
                if (containedGroup2 == null)
                {//2だけ新顔-->1のグループに吸収
                    containedGroup1.Add(bm2);
                }
                else
                {//二つとも所属済み-->二つのグループを合併
                    if (containedGroup1 == containedGroup2) return;
                    displayGroupList.Remove(containedGroup2);
                    foreach (object o in containedGroup2) containedGroup1.Add(o);
                }
            }

        }
        public GraphList IsContained(object o)
        {
            foreach (GraphList al in displayGroupList)
            {
                if (al.Contains(o)) return al;
            }
            return null;
        }
        private void ReplaceBm2Name()
        {
            foreach (ArrayList al in displayGroupList)
            {
                for (int i = 0; i < al.Count; i++)
                {
                    BitmapElement be = (BitmapElement)al[i];
                    al.RemoveAt(i);
                    al.Insert(i, displayTable[be]);
                }
            }
        }
        #endregion

        #region graph
        private void MakeGraph() {
            if (displayTable.Count < 2) return;
            foreach (FigureElement fe in kotodama.Presentation.CurrentSlide.ElementList)
            {
                if (fe is ArrowElement)
                {
                    ArrowElement ae = fe as ArrowElement;
                    GraphList fromNode = IsContained(ae.fromElement);
                    GraphList towardNode = IsContained(ae.towardElement);
                    if (fromNode != null && towardNode != null)
                    {
                        if (!fromNode.Directing.Contains(towardNode))
                            fromNode.Directing.Add(towardNode);
                        if (!towardNode.Directed.Contains(fromNode))
                            towardNode.Directed.Add(fromNode);
                    }
                }
            }
            MakeEquals();
        }

        private void MakeEquals()
        {
            Queue<GraphList> equalQueue = new Queue<GraphList>();
            //List<GraphList> gList = new List<GraphList>();
            //IEnumerator enume = displayTable.Keys.GetEnumerator();
            //while (enume.MoveNext())
            //{
            //    gList.Add((GraphList)enume.Current);
            //}
            foreach (GraphList gl in displayGroupList)// gList)
            {
                foreach (GraphList gld in gl.Directing)
                {
                    if(gld.Directing.Contains(gl)){
                        equalQueue.Enqueue(gl);
                        equalQueue.Enqueue(gld);
                        //このとき、directedにも同じような循環関係がある。
                    }
                }
            }

            while (equalQueue.Count > 0)
            {
                GraphList gl1 = equalQueue.Dequeue();
                GraphList gl2 = equalQueue.Dequeue();
                gl1.Directing.Remove(gl2);
                gl2.Directing.Remove(gl1);
                //このとき、directedにも同じような循環関係がある。
                gl1.Directed.Remove(gl2);
                gl2.Directed.Remove(gl1);
                gl1.Equal.Add(gl2);
                gl2.Equal.Add(gl1);
            }

            //gList.Clear();
            //gList = null;
            equalQueue.Clear();
            equalQueue = null;
        }
        #endregion
    }

    public class GraphList : ArrayList
    {
        public List<GraphList> Directed;
        public List<GraphList> Directing;
        public List<GraphList> Equal;
        public GraphList()
            : base()
        {
            Directed = new List<GraphList>();
            Directing = new List<GraphList>();
            Equal = new List<GraphList>();
        }
    }

}
